﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.SharePoint.Administration;
using System.Diagnostics;

namespace AgileShare.Framework.Core
{
    /// <summary>
    /// Trace Security Levels
    /// </summary>
	public enum TraceProviderSeverity
	{
		CriticalError,
		Error,
		Warning,
		Monitorable,
		Information
	}

	public enum TraceProviderEventIds : uint
	{
		WebParts = 5000,
		Feature = 5001,
		SPListHelper = 5002,
		SPContentTypeHelper = 5003,
		TermStoreHelper = 5004,
		SPListItemHelper = 5005,
		SPWebHelper = 5006,
		CamlQueries = 5007,
		SiteProvisioning = 5008,
		SPFieldHelper = 5009,
		Entities = 5010
	}

	public static class TraceProviderCategories
	{
        public const string Default = "AgileShare.Framework";
	}

    /// <summary>
    /// Provides a framework for generating trace messages or trace events.
    /// 生成跟踪消息或跟踪事件提供了一个框架。
    /// </summary>
	public static class TraceProvider
	{
		/// <summary>
		/// Logs the specified message with standard severities.
        /// 记录指定的消息与标准的严重性。
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="severity">The severity.</param>
		public static void Log(string message, string categoryName, uint eventId, TraceProviderSeverity severity)
		{
			Log(message, categoryName, eventId, severity, null);
		}

		/// <summary>
		/// Logs the specified message with standard severities.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="severity">The severity.</param>
		/// <param name="exception">The exception.</param>
		public static void Log(string message, string categoryName, uint eventId, TraceProviderSeverity severity, Exception exception)
		{
			switch (severity)
			{
				case TraceProviderSeverity.CriticalError:
					WriteEvent(message, categoryName, eventId, TraceSeverity.High, EventSeverity.ErrorCritical, exception);
					break;
				case TraceProviderSeverity.Error:
					WriteEvent(message, categoryName, eventId, TraceSeverity.High, EventSeverity.Error, exception);
					break;
				case TraceProviderSeverity.Warning:
					WriteEvent(message, categoryName, eventId, TraceSeverity.Medium, EventSeverity.Warning, exception);
					break;
				case TraceProviderSeverity.Monitorable:
					WriteTrace(message, categoryName, eventId, TraceSeverity.Monitorable, exception);
					break;
				case TraceProviderSeverity.Information:
					WriteTrace(message, categoryName, eventId, TraceSeverity.Verbose, exception);
					break;
			}
		}

		/// <summary>
		/// Logs the specified message with standard severities.
		/// </summary>		
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="severity">The severity.</param>
		/// <param name="message">The message.</param>
		/// <param name="args">The parameters to include in the message.</param>
		public static void Log(string categoryName, uint eventId, TraceProviderSeverity severity, string message, params object[] args)
		{
			Log(string.Format(message, args), categoryName, eventId, severity);
		}

		/// <summary>
		/// Logs the specified message with standard severities.
		/// </summary>		
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="severity">The severity.</param>
		/// <param name="exception">The exception.</param>
		/// <param name="message">The message.</param>
		/// <param name="args">The parameters to include in the message.</param>
		public static void Log(string categoryName, uint eventId, TraceProviderSeverity severity, Exception exception, string message, params object[] args)
		{
			Log(string.Format(message, args), categoryName, eventId, severity, exception);
		}

		/// <summary>
		/// Writes the event. Use of 'Log' method for standard severities.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="traceSeverity">The trace severity.</param>
		public static void WriteTrace(string message, string categoryName, uint eventId, TraceSeverity traceSeverity)
		{
			WriteTrace(message, categoryName, eventId, traceSeverity, null);
		}

		/// <summary>
		/// Writes the event. Use of 'Log' method for standard severities.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="traceSeverity">The trace severity.</param>
		/// <param name="exception">The exception.</param>
		public static void WriteTrace(string message, string categoryName, uint eventId, TraceSeverity traceSeverity, Exception exception)
		{
			if (exception != null)
			{
				string errorMessage = GetErrorMessage(exception, null, exception.Message);
				message += "\r\n" + errorMessage;
			}
			
			try
			{
				Debug.WriteLine(message, categoryName);

				SPDiagnosticsCategory diagnosticsCategory = new SPDiagnosticsCategory(categoryName, traceSeverity, EventSeverity.None);				
				SPDiagnosticsService.Local.WriteTrace(eventId, diagnosticsCategory, traceSeverity, message, null);				
			}
			catch
			{
				// TODO: Where to log exception?
			}
		}

		/// <summary>
		/// Writes the event. Use of 'Log' method for standard severities.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="traceSeverity">The trace severity.</param>
		/// <param name="eventSeverity">The event severity.</param>
		public static void WriteEvent(string message, string categoryName, uint eventId, TraceSeverity traceSeverity, EventSeverity eventSeverity)
		{
			WriteEvent(message, categoryName, eventId, traceSeverity, eventSeverity, null);
		}

		/// <summary>
		/// Writes the event. Use of 'Log' method for standard severities.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <param name="eventId">The event id.</param>
		/// <param name="traceSeverity">The trace severity.</param>
		/// <param name="eventSeverity">The event severity.</param>
		/// <param name="exception">The exception.</param>
		public static void WriteEvent(string message, string categoryName, uint eventId, TraceSeverity traceSeverity, EventSeverity eventSeverity, Exception exception)
		{
			if (exception != null)
			{
				string errorMessage = GetErrorMessage(exception, null, exception.Message);
				message += "\r\n" + errorMessage;
			}		
			
			try			
			{
				Debug.WriteLine(message, categoryName);

				SPDiagnosticsCategory diagnosticsCategory = new SPDiagnosticsCategory(categoryName, traceSeverity, eventSeverity);
				
				SPDiagnosticsService.Local.WriteTrace(eventId, diagnosticsCategory, traceSeverity, message, null);
				SPDiagnosticsService.Local.WriteEvent((ushort)eventId, diagnosticsCategory, eventSeverity, message, null);				
			}
			catch
			{
				// TODO: Where to log exception?
			}
		}

		/// <summary>
		/// Gets the error message.
		/// </summary>
		/// <param name="exception">The exception.</param>
		/// <param name="sourceText">The source text.</param>
		/// <param name="titleText">The title text.</param>
		/// <returns></returns>
		public static string GetErrorMessage(Exception exception, string sourceText, string titleText)
		{
			System.Text.StringBuilder message = new StringBuilder();
			message.Append(titleText);
			message.Append(" - The details are below:   \n");
			message.Append("Type:         " + exception.GetType() + "   \n");
			message.Append("Function:     " + exception.TargetSite + "   \n");
			message.Append("Source:       " + exception.Source + "   \n");
			message.Append("Message:      " + exception.Message + "   \n");
			message.Append("Time:         " + DateTime.Now.ToString("G") + "   \n");

			if (sourceText != "")
			{
				message.Append("   \nCustom Message:   \n" + sourceText + "   \n");
			}

			message.Append("   \nStack Trace:   \n   \n");
			message.Append(exception.StackTrace);

			message.Append(GetInnerExceptions(exception));

			return message.ToString();
		}

		private static string GetInnerExceptions(Exception ex)
		{
			StringBuilder sb = new StringBuilder();
			int innerExceptionCount = 0;
			Exception currentInnerException = ex.InnerException;
			while (currentInnerException != null)
			{
				innerExceptionCount++;
				sb.AppendLine("\n\nINNER EXCEPTION " + innerExceptionCount);
				sb.AppendLine("Type:      " + currentInnerException.GetType());
				sb.AppendLine("Function:  " + currentInnerException.TargetSite);
				sb.AppendLine("Source:    " + currentInnerException.Source);
				sb.AppendLine("Message:   " + currentInnerException.Message);
				sb.AppendLine();
				sb.AppendLine("Stack Trace:");
				sb.AppendLine();
				sb.Append(currentInnerException.StackTrace);

				currentInnerException = currentInnerException.InnerException;
			}
			return sb.ToString();
		}
	}
}
